/*---------------------------------------------------------------------------
HostListManager.m -- Copyright (c) 1991 Rex Pruess
  
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA or send
   electronic mail to the the author.
  
This is the methods file for the HostListManager (HLM) class.  A HLM object is
responsible for managing the internal linked host list and the host
configuration file.
  
Rex Pruess <rpruess@umaxc.weeg.uiowa.edu>
  
$Header: /rpruess/apps/Remotes3.0/RCS/HostListManager.m,v 3.0 92/09/23 21:49:40 rpruess Exp $
-----------------------------------------------------------------------------
$Log:	HostListManager.m,v $
Revision 3.0  92/09/23  21:49:40  rpruess
Upgraded for NeXT System Release 3.0.  Primary changes were to support the
Terminal application under NeXT System Release 3.0.

Revision 2.1  92/09/21  11:20:13  rpruess
Added code to set the window title and mini-window titles for Stuart
sessions.

Revision 2.0  91/01/22  15:32:47  rpruess
Remotes-2.0 was upgraded for NeXT System Release 2.0 (standard or extended).
Remotes-2.0 supports the NeXT supplied Terminal application and the Stuart
shareware product.

Revision 1.1  90/04/10  14:25:48  rpruess
Initial revision

-----------------------------------------------------------------------------*/

/* Standard C header files */
#include <ctype.h>
#include <libc.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/file.h>

/* Private C header files */
#include "remsubs.h"

/* Host List Manager class header files */
#import "HostListManager.h"
#import "StuartSpeaker.h"

/* Appkit header files */
#import <appkit/Application.h>
#import <appkit/Control.h>
#import <appkit/Panel.h>
#import <appkit/PopUpList.h>

#import <defaults/defaults.h>

@implementation HostListManager

/*---------------------------------------------------------------------------
Override the new method so the object can be properly initialized.  This
initialization includes reading the configuration file & loading the hosts
into the internal linked list.
-----------------------------------------------------------------------------*/
- init
{   
   self = [super init];

   [self initHLMObject:self];   /* Initialize linked host lists */
   return self;
}

/*---------------------------------------------------------------------------
Initialize the host list.
-----------------------------------------------------------------------------*/
- initHLMObject:sender
{
   short int       appType;
   short int       autoLaunch;
   char            buffer[MAXRECLEN + 1];
   char           *bufPtr;
   char            buttonName[MAXPOPLEN + 1];
   const char     *defaultsDebugLevel;
   int             code;
   short int       columns;
   char            field[MAXRECLEN + 1];
   unsigned int    flagValues;
   short int       fontType;
   short int       fontSize;
   char            hName[MAXHOSTNAMELEN + 1];
   int             i;
   short int       lines;
   short int       metaNum;
   BOOL            oldStyle;
   short int       protocolType;
   int             recordNum;
   NXSize          screenSize;  /* Screen width/height */
   char            userID[MAXIDLEN + 1];
   short int       versionNum;
   short int       x;
   short int       y;
   FILE           *fp;

   /* Set debugging value from defaults data base */
   defaultsDebugLevel = NXGetDefaultValue ([NXApp appName], "DebugLevel");

   if (strcmp (defaultsDebugLevel, "low") == 0)
      debugLevel = DEBUGLOW;
   else if (strcmp (defaultsDebugLevel, "high") == 0)
      debugLevel = DEBUGHIGH;
   else if (strcmp (defaultsDebugLevel, "max") == 0)
      debugLevel = DEBUGMAX;
   else
      debugLevel = DEBUGOFF;

   /* Load linked host list. */
   recordNum = 0;
   nHosts = 0;

   fp = [self openConfigFile:"r"];

   if (fp == NULL) {            /* In no hosts found, then force one! */
      code = [self addLocalHost:self];
      return self;
   }

   /* Read & process each record from the config file */
   while (fgets (buffer, MAXRECLEN, fp) != NULL) {
      bufPtr = buffer;          /* Pointer to next field in buffer */
      recordNum++;

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"version number" recNum:recordNum];
         continue;
      }
      field[MAXPOPLEN] = '\0';
      
      oldStyle = NO;		/* Old style config file has no version # */
      for (i = 0; i < strlen (field); i++) {
	 if (isdigit (field[i]))
	    continue;
	 else {
	    oldStyle = YES;
	    break;
	 }
      }
      
      if (oldStyle) {
	 versionNum = 0;
	 strcpy (buttonName, field);
      }
      else {
	 versionNum = atoi (field);
	 
	 if ((bufPtr = getfield (bufPtr, field)) == NULL) {
	    [self errPrint:"button name" recNum:recordNum];
	    continue;
	 }
	 field[MAXPOPLEN] = '\0';
	 strcpy (buttonName, field);
      }
      
      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"hostname" recNum:recordNum];
         continue;
      }
      field[MAXHOSTNAMELEN] = '\0';
      strcpy (hName, field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"user ID" recNum:recordNum];
         continue;
      }
      field[MAXIDLEN] = '\0';
      strcpy (userID, field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"application" recNum:recordNum];
         continue;
      }
      appType = atoi (field);
      if (appType != TERMINAL && appType != STUART) {
         [self errPrint:"application" recNum:recordNum];
         continue;
      }

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"protocol" recNum:recordNum];
         continue;
      }
      protocolType = atoi (field);
      if (protocolType != RLOGIN && protocolType != TELNET
         && protocolType != TN3270) {
         [self errPrint:"protocol" recNum:recordNum];
         continue;
      }

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"x" recNum:recordNum];
         continue;
      }
      x = atoi (field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"y" recNum:recordNum];
         continue;
      }
      y = atoi (field);
      
      /* In old version, Y was measured from top of display. */
      if (versionNum == 0 && y < 300) {
	 [NXApp getScreenSize:&screenSize];
	 y = screenSize.height - y;
      }
      
      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"lines" recNum:recordNum];
         continue;
      }
      lines = atoi (field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"columns" recNum:recordNum];
         continue;
      }
      columns = atoi (field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"font type" recNum:recordNum];
         continue;
      }
      fontType = atoi (field);
      if (fontType != COURIER && fontType != OHLFS) {
         [self errPrint:"font type" recNum:recordNum];
         continue;
      }

      if ((bufPtr = getfield (bufPtr, field)) == NULL) {
         [self errPrint:"font size" recNum:recordNum];
         continue;
      }
      fontSize = atoi (field);

      if ((bufPtr = getfield (bufPtr, field)) == NULL)
         autoLaunch = NO;
      else
         autoLaunch = atoi (field);

      if (appType == TERMINAL)
	 flagValues = TTRANSLAT | TAUTOWRAP | TSCROLLBK;
      else
	 flagValues = SSCROLLBK | STRANSLAT | STERMSPAC;
      
      metaNum = METAESC;

      if (versionNum == CFGVERSION) {
	 if ((bufPtr = getfield (bufPtr, field)) == NULL) {
	    [self errPrint:"flag values" recNum:recordNum];
	    continue;
	 }
	 flagValues = atoi (field);

	 if ((bufPtr = getfield (bufPtr, field)) == NULL) {
	    [self errPrint:"meta number" recNum:recordNum];
	    continue;
	 }
	 metaNum = atoi (field);
      }
	 
      code = [self addHost:buttonName fullHostName:hName loginName:userID
         application:appType protocol:protocolType locX:x locY:y
         nLines:lines nCols:columns font:fontType fontSize:fontSize
         autoStart:autoLaunch flags:flagValues meta:metaNum
	 updateConfigFile:NO];

      if (debugLevel == DEBUGMAX) {
         printf ("%s:(read #%d okay) version:%d\t%s\t%s\n",[NXApp appName],
            recordNum, versionNum, buttonName, hName);
         printf ("\tID:%s", userID);
         printf ("\tapp:%d", appType);
         printf ("\tprot:%d", protocolType);
         printf ("\tx:%d", x);
         printf ("\ty:%d", y);
         printf ("\n");
         printf ("\tlines:%d", lines);
         printf ("\tcols:%d", columns);
         printf ("\tfont:%d", fontType);
         printf ("\tfontsize:%d", fontSize);
         printf ("\n");
         printf ("\tautostart:%d", autoLaunch);
         printf ("\tflags:%u", flagValues);
         printf ("\tmeta:%d", metaNum);
         printf ("\n");
      }
   }

   fclose (fp);

   if (debugLevel >= DEBUGHIGH)
      printf ("%s: %d records successfully read from config file.\n",
         [NXApp appName], nHosts);

   return self;
}

/*---------------------------------------------------------------------------
Stuff an entry into the doubly linked circular list of hosts.  The head and
tail entries point to each other so a true circular loop is maintained & thus
simplifies coding.  Duplicates are not entered.  This routine returns the slot
number where the host was inserted.  The list is maintained in alphabetical
order.
-----------------------------------------------------------------------------*/
- (int)insert:(struct hostEntry *) newEntry
{
   int             slotNum;     /* Slot position of host in linked list */
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   /* First entry in the list.  Point to myself. */
   if (nHosts == 0) {
      newEntry -> nextHost = newEntry;
      newEntry -> prevHost = newEntry;
      begHost = newEntry;       /* Set beginning host pointer too. */
      nHosts++;
      return 0;
   }

   /* Find spot that this entry must be inserted at. */
   hostPtr = begHost;
   for (slotNum = 0; slotNum < nHosts; slotNum++) {
      if (strcmp (newEntry -> popUpName, hostPtr -> popUpName) < 0)
         break;
      if (strcmp (newEntry -> popUpName, hostPtr -> popUpName) == 0)
         return -1;
      hostPtr = hostPtr -> nextHost;
   }

   /* Entry is at head of list.  Adjust beginning host pointer too. */
   if (slotNum == 0)
      begHost = newEntry;

   /* Set new entry's next & previous pointers */
   newEntry -> nextHost = hostPtr;
   newEntry -> prevHost = hostPtr -> prevHost;

   /* Set previous host's "next pointer" */
   hostPtr -> prevHost -> nextHost = newEntry;

   /* Set next host's "previous pointer" */
   hostPtr -> prevHost = newEntry;

   nHosts++;

   return slotNum;
}

/*---------------------------------------------------------------------------
Print error message about bad input record in configuration file.
-----------------------------------------------------------------------------*/
- errPrint:(char *)fieldName recNum:(int)recordNum
{
   printf ("%s:Invalid %s field in record %d in config file.\n",
      [NXApp appName], fieldName, recordNum);
   return self;
}

/*---------------------------------------------------------------------------
Add a host into the internal linked list.
-----------------------------------------------------------------------------*/
- (int)addHost:(const char *)buttonName
   fullHostName:(const char *)fullName
   loginName:(const char *)userID
   application:(short int)app
   protocol:(short int)prot
   locX:(short int)x
   locY:(short int)y
   nLines:(short int)lines
   nCols:(short int)columns
   font:(short int)aFont
   fontSize:(short int)fontNum
   autoStart:(short int)autoLaunch
   flags:(unsigned int)flagValues
   meta:(short int)metaNum
   updateConfigFile:(short int)updConfigFile
{
   int             slotNum;     /* Slot position of host in linked list */
   struct hostEntry *newEntry;  /* Pointer to host entry */
   FILE           *fp;

   newEntry = malloc (HOSTENTRYLEN);

   strcpy (newEntry -> popUpName, buttonName);
   strcpy (newEntry -> hostName, fullName);
   strcpy (newEntry -> loginName, userID);
   newEntry -> appType = app;
   newEntry -> protocolType = prot;
   newEntry -> locX = x;
   newEntry -> locY = y;
   newEntry -> nLines = lines;
   newEntry -> nCols = columns;
   newEntry -> fontType = aFont;
   newEntry -> fontSize = fontNum;
   newEntry -> autoStart = autoLaunch;
   newEntry -> flags = flagValues;
   newEntry -> meta = metaNum;

   slotNum = [self insert:newEntry];

   if (slotNum < 0 || updConfigFile == NO)
      return slotNum;

   fp = [self openConfigFile:"a"];

   if (fp != NULL) {
      fprintf (fp, "%d", CFGVERSION);      
      fprintf (fp, "\t%s", buttonName);
      fprintf (fp, "\t%s", fullName);
      fprintf (fp, "\t%s", userID);
      fprintf (fp, "\t%d", app);
      fprintf (fp, "\t%d", prot);
      fprintf (fp, "\t%d", x);
      fprintf (fp, "\t%d", y);
      fprintf (fp, "\t%d", lines);
      fprintf (fp, "\t%d", columns);
      fprintf (fp, "\t%d", aFont);
      fprintf (fp, "\t%d", fontNum);
      fprintf (fp, "\t%d", autoLaunch);
      fprintf (fp, "\t%u", flagValues);
      fprintf (fp, "\t%d", metaNum);
      fprintf (fp, "\n");
   }

   fclose (fp);

   if (debugLevel >= DEBUGHIGH)
      printf ("%s: Record added to config file.\n",[NXApp appName]);

   if (debugLevel == DEBUGMAX) {
      printf ("%s:(append okay) version:%d\t%s\t%s\n",
         [NXApp appName], CFGVERSION, buttonName, fullName);
      printf ("\tID:%s", userID);
      printf ("\tapp:%d", app);
      printf ("\tprot:%d", prot);
      printf ("\tx:%d", x);
      printf ("\ty:%d", y);
      printf ("\n");
      printf ("\tlines:%d", lines);
      printf ("\tcols:%d", columns);
      printf ("\tfont:%d", aFont);
      printf ("\tfontsize:%d", fontNum);
      printf ("\n");
      printf ("\tautostart:%d", autoLaunch);
      printf ("\tflags:%u", flagValues);
      printf ("\tmeta:%d", metaNum);
      printf ("\n");
   }

   return slotNum;
}

/*---------------------------------------------------------------------------
Add a default Terminal & Stuart entry.  Use values in the defaults data base
for the various fields.
-----------------------------------------------------------------------------*/
- (int)addLocalHost:sender
{
   short int       columns;
   short int       lines;
   unsigned int    flagValues;
   short int       fontNum;
   short int       fontStyle;
   int             slotNum;     /* Slot position of host in linked list */
   const char     *string;      /* Temporary variable */

   string = NXGetDefaultValue ("Terminal", "Columns");
   columns = atoi (string);

   string = NXGetDefaultValue ("Terminal", "Rows");
   lines = atoi (string);

   string = NXGetDefaultValue ("Terminal", "NXFixedPitchFontSize");
   fontNum = atoi (string);

   string = NXGetDefaultValue ("Terminal", "NXFixedPitchFont");
   if (strcmp (string, "Courier") == 0)
      fontStyle = COURIER;
   else
      fontStyle = OHLFS;

   flagValues = TTRANSLAT | TAUTOWRAP | TSCROLLBK | TSOURCEDL;

   slotNum = [self addHost:"Terminal" fullHostName:"localhost" loginName:""
      application:TERMINAL protocol:RLOGIN locX:120 locY:832 nLines:lines
      nCols:columns font:fontStyle fontSize:fontNum
      autoStart:NO flags:flagValues meta:METAESC updateConfigFile:YES];

   string = NXGetDefaultValue ("Stuart", "Columns");
   columns = atoi (string);

   string = NXGetDefaultValue ("Stuart", "Lines");
   lines = atoi (string);

   string = NXGetDefaultValue ("Stuart", "NXFixedPitchFontSize");
   fontNum = atoi (string);

   string = NXGetDefaultValue ("Stuart", "NXFixedPitchFont");
   if (strcmp (string, "Courier") == 0)
      fontStyle = COURIER;
   else
      fontStyle = OHLFS;

   flagValues = SSCROLLBK | STRANSLAT | STERMSPAC | SSOURCEDL;
   slotNum = [self addHost:"Stuart" fullHostName:"localhost" loginName:""
      application:STUART protocol:RLOGIN locX:130 locY:832 nLines:lines
      nCols:columns font:fontStyle fontSize:fontNum
      autoStart:NO flags:flagValues meta:METAESC updateConfigFile:YES];

   return slotNum;
}

/*---------------------------------------------------------------------------
Change values in an existing host entry.
-----------------------------------------------------------------------------*/
- (int)changeHost:(const char *)buttonName
   fullHostName:(const char *)fullName
   loginName:(const char *)userID
   application:(short int)app
   protocol:(short int)prot
   locX:(short int)x
   locY:(short int)y
   nLines:(short int)lines
   nCols:(short int)columns
   font:(short int)aFont
   fontSize:(short int)fontNum
   autoStart:(short int)autoLaunch
   flags:(unsigned int)flagValues
   meta:(short int)metaNum
{
   int             i;
   int             slotNum;     /* Slot position of host in linked list */
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   slotNum = [self getHostSlotNum:buttonName];
   if (slotNum < 0)
      return slotNum;

   /* Find the host in our list */
   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;

   strcpy (hostPtr -> popUpName, buttonName);
   strcpy (hostPtr -> hostName, fullName);
   strcpy (hostPtr -> loginName, userID);
   hostPtr -> appType = app;
   hostPtr -> protocolType = prot;
   hostPtr -> locX = x;
   hostPtr -> locY = y;
   hostPtr -> nLines = lines;
   hostPtr -> nCols = columns;
   hostPtr -> fontType = aFont;
   hostPtr -> fontSize = fontNum;
   hostPtr -> autoStart = autoLaunch;
   hostPtr -> flags = flagValues;
   hostPtr -> meta = metaNum;

   [self writeConfigFile:self];

   return slotNum;
}

/*---------------------------------------------------------------------------
Delete a host entry from the internal linked list & from the file.
-----------------------------------------------------------------------------*/
- (int)deleteHost:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   if (slotNum == 0)
      begHost = hostPtr -> nextHost;	/* Head entry deletion */
   else
      for (i = 0; i < slotNum; i++)
         hostPtr = hostPtr -> nextHost;

   hostPtr -> nextHost -> prevHost = hostPtr -> prevHost;
   hostPtr -> prevHost -> nextHost = hostPtr -> nextHost;

   free (hostPtr);

   nHosts--;

   [self writeConfigFile:self];

   return slotNum;
}

/*---------------------------------------------------------------------------
Return the application type (e.g., Terminal, Stuart) for this slot.
-----------------------------------------------------------------------------*/
- (int)getAppType:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> appType;
}

/*---------------------------------------------------------------------------
Return the auto launch value for this slot.
-----------------------------------------------------------------------------*/
- (int)getAutoStart:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> autoStart;
}

/*---------------------------------------------------------------------------
Return the button name for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (char *)getButtonName:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> popUpName;
}

/*---------------------------------------------------------------------------
Return the width for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getColumns:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> nCols;
}

/*---------------------------------------------------------------------------
Return the configuration pathname for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (const char *)getConfigFile:sender
{
   return NXGetDefaultValue ([NXApp appName], "ConfigFile");
}

/*---------------------------------------------------------------------------
Return the debugging level.
-----------------------------------------------------------------------------*/
- (int)getDebugLevel:sender
{
   return debugLevel;
}

/*---------------------------------------------------------------------------
Return the Stuart/Terminal flag values.
-----------------------------------------------------------------------------*/
- (unsigned int)getFlags:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> flags;
}

/*---------------------------------------------------------------------------
Return the font type for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getFontType:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> fontType;
}

/*---------------------------------------------------------------------------
Return the font size for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getFontSize:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> fontSize;
}

/*---------------------------------------------------------------------------
Return the complete host name for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (char *)getFullHostName:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> hostName;
}

/*---------------------------------------------------------------------------
Return the host slot number for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getHostSlotNum:(const char *)buttonName
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < nHosts; i++) {
      if (strcmp (buttonName, hostPtr -> popUpName) == 0)
         return i;
      hostPtr = hostPtr -> nextHost;
   }

   return -1;
}

/*---------------------------------------------------------------------------
Return the number of lines in the window for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getLines:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> nLines;
}

/*---------------------------------------------------------------------------
Return the meta value.
-----------------------------------------------------------------------------*/
- (int)getMeta:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> meta;
}

/*---------------------------------------------------------------------------
Return the number of hosts in list.
-----------------------------------------------------------------------------*/
- (int)getNumHosts:sender
{
   return nHosts;
}

/*---------------------------------------------------------------------------
Return the popup button name for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (char *)getPopUpName:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> popUpName;
}

/*---------------------------------------------------------------------------
Return the protocol type (e.g., rlogin, telnet, tn3270).
-----------------------------------------------------------------------------*/
- (int)getProtocolType:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> protocolType;
}

/*---------------------------------------------------------------------------
Return the user ID for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (char *)getUserID:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> loginName;
}

/*---------------------------------------------------------------------------
Return the X coordinate for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getX:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> locX;
}

/*---------------------------------------------------------------------------
Return the Y coordinate for the entry at the specified slot.
-----------------------------------------------------------------------------*/
- (int)getY:(int)slotNum
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;
   return hostPtr -> locY;
}

/*---------------------------------------------------------------------------
Load a popuplist.  This could be either the hosts popup list or the
configuration pulldown list.
-----------------------------------------------------------------------------*/
- loadPopUpList:aPopUpList
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < nHosts; i++) {
      [aPopUpList addItem:hostPtr -> popUpName];
      hostPtr = hostPtr -> nextHost;
   }

   return self;
}

/*---------------------------------------------------------------------------
Remotes is coming to life.  Launch those automatic startup guys.
-----------------------------------------------------------------------------*/
- autoStart:sender
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */

   hostPtr = begHost;

   for (i = 0; i < nHosts; i++) {
      if (hostPtr -> autoStart == YES)
         [self loginToHost:hostPtr -> popUpName activate:NO];

      hostPtr = hostPtr -> nextHost;
   }

   return self;
}

/*---------------------------------------------------------------------------
Fire up a Terminal or Stuart session for the user.
-----------------------------------------------------------------------------*/
- loginToHost:(const char *)buttonName activate:(BOOL)actWindow
{
   char            colsStr[16];
   char           *comArgs[MAXCOMARGS];
   char            command[1024];
   char            errMsg[256];
   int             i;
   int             ind;
   char            fontStr[16];
   char            linesStr[16];
   char            loginStr[256];
   char            metaStr[16];
   int             pid;
   char           *shellPtr;
   id              stuart;
   int             slotNum;
   char          **tempPtr;
   char            xStr[16];
   char            yStr[16];

   int             vfork (void);

   struct hostEntry *hostPtr;   /* Pointer to host entry */

   slotNum = [self getHostSlotNum:buttonName];
   if (slotNum < 0) {
      NXRunAlertPanel (NULL, "Button name '%s' not found.", NULL, NULL, NULL, buttonName);
      return self;
   }

   ind = 0;
   
   hostPtr = begHost;
   for (i = 0; i < slotNum; i++)
      hostPtr = hostPtr -> nextHost;

   if (hostPtr -> appType == TERMINAL) {
      strcpy (command, "/NextApps/Terminal.app/Terminal");

      comArgs[ind++] = "Terminal";

      comArgs[ind++] = "-Shell";

      if (strcmp (hostPtr -> hostName, "localhost") == 0) {
	 shellPtr = getenv ("SHELL");
	 if (shellPtr == NULL)
	    comArgs[ind++] = "/bin/csh";
	 else
	    comArgs[ind++] = shellPtr;
      }
      else {
         switch (hostPtr -> protocolType) {
         case RLOGIN:
            strcpy (loginStr, "\"/usr/ucb/rlogin ");
            strcat (loginStr, hostPtr -> hostName);
	    
            if (*hostPtr -> loginName != '\0') {
               strcat (loginStr, " -l ");
               strcat (loginStr, hostPtr -> loginName);
            }

            break;

         case TELNET:
            strcpy (loginStr, "\"/usr/ucb/telnet ");
            strcat (loginStr, hostPtr -> hostName);
            break;

         case TN3270:
            strcpy (loginStr, "\"/usr/ucb/tn3270 ");
            strcat (loginStr, hostPtr -> hostName);
            break;

         default:
            break;
         }

	 strcat (loginStr, "\"");
	 comArgs[ind++] = loginStr;
      }

      comArgs[ind++] = "-WinLocX";
      sprintf (xStr, "%d", hostPtr -> locX);
      comArgs[ind++] = xStr;

      comArgs[ind++] = "-WinLocY";
      sprintf (yStr, "%d", hostPtr -> locY);
      comArgs[ind++] = yStr;

      comArgs[ind++] = "-Rows";
      sprintf (linesStr, "%d", hostPtr -> nLines);
      comArgs[ind++] = linesStr;

      comArgs[ind++] = "-Columns";
      sprintf (colsStr, "%d", hostPtr -> nCols);
      comArgs[ind++] = colsStr;

      comArgs[ind++] = "-NXFixedPitchFont";
      if (hostPtr -> fontType == COURIER)
	 comArgs[ind++] = "Courier";
      else
	 comArgs[ind++] = "Ohlfs";

      comArgs[ind++] = "-NXFixedPitchFontSize";
      sprintf (fontStr, "%d", hostPtr -> fontSize);
      comArgs[ind++] = fontStr;

      if (hostPtr -> meta != TMETADEF) {
	 comArgs[ind++] = "-Meta";
	 sprintf (metaStr, "%d", hostPtr -> meta);
	 comArgs[ind++] = metaStr;
      }

      comArgs[ind++] = "-Translate";
      comArgs[ind++] = (hostPtr -> flags & TTRANSLAT) ? "YES" : "NO";
      
      comArgs[ind++] = "-Keypad";
      comArgs[ind++] = (hostPtr -> flags & TKEYPAD) ? "YES" : "NO";
      
      comArgs[ind++] = "-StrictEmulation";
      comArgs[ind++] = (hostPtr -> flags & TSTRICTEM) ? "YES" : "NO";
      
      comArgs[ind++] = "-Autowrap";
      comArgs[ind++] = (hostPtr -> flags & TAUTOWRAP) ? "YES" : "NO";
      
      comArgs[ind++] = "-Scrollback";
      comArgs[ind++] = (hostPtr -> flags & TSCROLLBK) ? "YES" : "NO";
      
      comArgs[ind++] = "-AutoFocus";
      comArgs[ind++] = (hostPtr -> flags & TAUTOFOCS) ? "YES" : "NO";
      
      comArgs[ind++] = "-SourceDotLogin";
      comArgs[ind++] = (hostPtr -> flags & TSOURCEDL) ? "YES" : "NO";

      comArgs[ind++] = "-CustomTitle";
      comArgs[ind++] = hostPtr -> hostName;

      comArgs[ind++] = NULL;

      /*** Gotta build a shell-like command under NeXT Release 3.1. */

      tempPtr = comArgs;
      while (*++tempPtr != NULL) {
	 strcat (command, " ");
	 strcat (command, *tempPtr);
      }
      
      if (debugLevel != DEBUGOFF)
	 printf ("%s: %s\n",[NXApp appName], command);

      pid = vfork ();
      if (pid == 0) {

	 system (command);
	 exit (0);

#if 0    /*** execve worked under NeXT Release 2.x.  Doesn't work now. */
	 code = execve (command, comArgs, environ);

	 perror ([NXApp appName]);
	 fprintf (stderr, "%s: Execve error code=%d.\n",[NXApp appName], code);

	 _exit (0);
#endif

      }
   }
   else {
      stuart = [StuartSpeaker new];

      if (debugLevel != DEBUGOFF)
	 printf ("Stuart");
	 
      if (strcmp (hostPtr -> hostName, "localhost") != 0) {

         switch (hostPtr -> protocolType) {
         case RLOGIN:
            strcpy (loginStr, "rlogin ");
            strcat (loginStr, hostPtr -> hostName);

            if (*hostPtr -> loginName != '\0') {
               strcat (loginStr, " -l ");
               strcat (loginStr, hostPtr -> loginName);
            }

            break;

         case TELNET:
            strcpy (loginStr, "telnet ");
            strcat (loginStr, hostPtr -> hostName);
            break;

         case TN3270:
            strcpy (loginStr, "tn3270 ");
            strcat (loginStr, hostPtr -> hostName);
            break;

         default:
            break;
         }

	 [self setStuartDefault:stuart default:"Shell" as:loginStr];
      }

      sprintf (xStr, "%d", hostPtr -> locX);
      [self setStuartDefault:stuart default:"WinLocX" as:xStr];

      sprintf (yStr, "%d", hostPtr -> locY);
      [self setStuartDefault:stuart default:"WinLocY" as:yStr];

      sprintf (linesStr, "%d", hostPtr -> nLines);
      [self setStuartDefault:stuart default:"Lines" as:linesStr];

      sprintf (colsStr, "%d", hostPtr -> nCols);
      [self setStuartDefault:stuart default:"Columns" as:colsStr];

      if (hostPtr -> fontType == COURIER)
	 [self setStuartDefault:stuart default:"NXFixedPitchFont" as:"Courier"];
      else
	 [self setStuartDefault:stuart default:"NXFixedPitchFont" as:"Ohlfs"];

      sprintf (fontStr, "%d", hostPtr -> fontSize);
      [self setStuartDefault:stuart default:"NXFixedPitchFontSize" as:fontStr];

      if (hostPtr -> meta != SMETADEF) {
	 sprintf (metaStr, "%d", hostPtr -> meta);
	 [self setStuartDefault:stuart default:"Meta" as:metaStr];
      }

      [self setStuartDefault:stuart default:"Strict" as:(hostPtr -> flags & SSTRICTEM) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"Keypad" as:(hostPtr -> flags & SKEYPAD) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"Scrollback" as:(hostPtr -> flags & SSCROLLBK) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"Translate" as:(hostPtr -> flags & STRANSLAT) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"Reverse" as:(hostPtr -> flags & SREVERSE) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"KeyboardFocus" as:(hostPtr -> flags & SKEYBDFOC) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"TerminalSpacing" as:(hostPtr -> flags & STERMSPAC) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"MouseFocus" as:(hostPtr -> flags & SMOUSEFOC) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"SourceDotLogin" as:(hostPtr -> flags & SSOURCEDL) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"TestExit" as:(hostPtr -> flags & STESTEXIT) ? "YES" : "NO"];
      [self setStuartDefault:stuart default:"WriteStuartrc" as:"NO"];
      [self setStuartDefault:stuart default:"Title" as:hostPtr -> hostName];
      [self setStuartDefault:stuart default:"MiniTitle" as:buttonName];
      [self setStuartDefault:stuart default:"Activate" as:actWindow ? "YES" : "NO"];

      if (debugLevel != DEBUGOFF)
	 printf("\n");
      
      if ([stuart stuartConnectAndNew]) {
	 strcpy (errMsg, "There was a problem communicating with the Stuart ");
	 strcat (errMsg, "application.  (Speaker port unavailable.)  This may ");
	 strcat (errMsg, "have occurred because Stuart is not installed on ");
	 strcat (errMsg, "your system or because Stuart was slow in starting.");

	 NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL);
      }
      
      [stuart free];
   }
   
   return self;
}

/*---------------------------------------------------------------------------
Set specified Stuart default value.
-----------------------------------------------------------------------------*/
- setStuartDefault:aStuart default:(const char *)defaultV as:(const char *)asV
{
   if (debugLevel != DEBUGOFF)
      printf(" -%s %s", defaultV, asV);

   [aStuart default:defaultV as:asV];

   return self;
}

/*---------------------------------------------------------------------------
Set debugging variable within object & in defaults data base.
-----------------------------------------------------------------------------*/
- setDebugLevel:(short int)debugNum
{
   int             code;
   char            defaultsDebugLevel[5];

   debugLevel = debugNum;       /* Update object's setting */

   if (debugNum == 1)
      strcpy (defaultsDebugLevel, "low");
   else if (debugNum == 2)
      strcpy (defaultsDebugLevel, "high");
   else
      strcpy (defaultsDebugLevel, "off");

   code = NXWriteDefault ([NXApp appName], "DebugLevel", defaultsDebugLevel);

   return self;
}

/*---------------------------------------------------------------------------
Open the configuration file.
-----------------------------------------------------------------------------*/
- (FILE *) openConfigFile:(const char *)mode
{
   const char     *configFile;  /* Configuration pathname */
   FILE           *fp;

   configFile = NXGetDefaultValue ([NXApp appName], "ConfigFile");

   if ((fp = fopen (configFile, mode)) == NULL) {
      if (debugLevel >= DEBUGHIGH)
         printf ("%s:Unable to open config file '%s' with mode '%s'.\n",
            [NXApp appName], configFile, mode);
      return NULL;
   }

   if (debugLevel >= DEBUGHIGH)
      printf ("%s:'%s' opened with mode '%s'.\n",
         [NXApp appName], configFile, mode);

   return fp;
}

/*---------------------------------------------------------------------------
Set configuration path variable within object & in defaults data base.
-----------------------------------------------------------------------------*/
- setConfigFile:(const char *)configFile
{
   const char     *configFileOld;
   FILE           *fp;

   if (strcmp (configFile,[self getConfigFile:self]) == 0)
      return self;

   if (index (configFile, ' ') != 0) {
      NXRunAlertPanel (NULL, "Blanks not allowed in pathname.", NULL, NULL,
         NULL);
      return self;
   }

   fp = fopen (configFile, "r");
   if (fp != NULL) {
      fclose (fp);
      if (NXRunAlertPanel (NULL, "The config file '%s' exists; OK to overwrite?",
	    "Cancel", "OK", NULL, configFile) == NX_ALERTDEFAULT)
         return self;
   }

   fp = fopen (configFile, "w");
   if (fp == NULL) {
      NXRunAlertPanel (NULL, "Unable to open the config file '%s'.", NULL, NULL, NULL, configFile);
      return self;
   }

   configFileOld = NXGetDefaultValue ([NXApp appName], "ConfigFile");
   if (NXWriteDefault ([NXApp appName], "ConfigFile", configFile) == 0) {
      NXRunAlertPanel (NULL, "Unable to write to defaults data base.", NULL, NULL,
         NULL);
      return self;
   }

   [self writeConfigFile:self];
   unlink (configFileOld);

   return self;
}

/*---------------------------------------------------------------------------
Write the configuration file from our internal list.
-----------------------------------------------------------------------------*/
- writeConfigFile:sender
{
   int             i;
   struct hostEntry *hostPtr;   /* Pointer to host entry */
   FILE           *fp;

   fp = [self openConfigFile:"w"];

   hostPtr = begHost;
   for (i = 0; i < nHosts; i++) {
      fprintf (fp, "%d", CFGVERSION);
      fprintf( fp, "\t%s", hostPtr -> popUpName); 
      fprintf( fp, "\t%s", hostPtr -> hostName); 
      fprintf (fp, "\t%s", hostPtr -> loginName);
      fprintf (fp, "\t%d", hostPtr -> appType);
      fprintf (fp, "\t%d", hostPtr -> protocolType);
      fprintf (fp, "\t%d", hostPtr -> locX);
      fprintf (fp, "\t%d", hostPtr -> locY);
      fprintf (fp, "\t%d", hostPtr -> nLines);
      fprintf (fp, "\t%d", hostPtr -> nCols);
      fprintf (fp, "\t%d", hostPtr -> fontType);
      fprintf (fp, "\t%d", hostPtr -> fontSize);
      fprintf (fp, "\t%d", hostPtr -> autoStart);
      fprintf (fp, "\t%u", hostPtr -> flags);
      fprintf (fp, "\t%d", hostPtr -> meta);
      fprintf (fp, "\n");

      if (debugLevel == DEBUGMAX) {
         printf ("%s:(write #%d okay) version:%d\t%s\t%s\n",
            [NXApp appName], i + 1, CFGVERSION, hostPtr -> popUpName, hostPtr -> hostName);
         printf ("\tID:%s", hostPtr -> loginName);
         printf ("\tapp:%d", hostPtr -> appType);
         printf ("\tprot:%d", hostPtr -> protocolType);
         printf ("\tx:%d", hostPtr -> locX);
         printf ("\ty:%d", hostPtr -> locY);
         printf ("\n");
         printf ("\tlines:%d", hostPtr -> nLines);
         printf ("\tcols:%d", hostPtr -> nCols);
         printf ("\tfont:%d", hostPtr -> fontType);
         printf ("\tfontsize:%d", hostPtr -> fontSize);
         printf ("\n");
         printf ("\tautostart:%d", hostPtr -> autoStart); 
	 printf ("\tflags:%u", hostPtr -> flags);
	 printf ("\tmeta:%d", hostPtr -> meta);
	 printf ("\n");
      }

      hostPtr = hostPtr -> nextHost;
   }

   fclose (fp);

   if (debugLevel >= DEBUGHIGH)
      printf ("%s: Config file written.  %d records in the file.\n",
         [NXApp appName], nHosts);

   return self;
}

@end
